home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 4 / Amiga Tools 4.iso / tools / internet-tools / httpproxy / httpproxy.c < prev    next >
C/C++ Source or Header  |  1996-02-26  |  58KB  |  1,959 lines

  1. /*(( "Header" */
  2. /*
  3.  * $Id: httpproxy.c,v 0.9 1995/12/06 19:53:55 mshopf Exp mshopf $
  4.  *
  5.  * (c) 1995 Matthias Hopf
  6.  *
  7.  * A small little Http Proxy.
  8.  * It can serv as a ProxyProxy, too (i.e. it can perform only caching and will
  9.  * get its data from another proxy).
  10.  * That way it can be used for other protocol types than html, too.
  11.  *
  12.  * Run it standalone at high priority. It won't need much computing time as
  13.  * it does no busy wait at all.
  14.  * If you really want to re-get an already cached page, just reload it immedeately.
  15.  * (no other request inbetween and no more than ReloadTime seconds delay).
  16.  * If you browse offline, you'll get a note that the cache is invalid. Reload the
  17.  * page if you want to queue the page and get the old cache.
  18.  */
  19.  
  20. /*
  21.  * $Log: httpproxy.c,v $
  22.  * Revision 0.9  1995/12/06  19:53:55  mshopf
  23.  * added fixes for unix machines and AmiTCP4.0 compilation.
  24.  * some bug fixes.
  25.  * more html conform now.
  26.  * lots of printf type fixes.
  27.  *
  28.  * Revision 0.8  1995/12/03  14:20:23  mshopf
  29.  * Made auto requests and proxy messages more http conform.
  30.  *
  31.  * Revision 0.7  1995/11/19  17:57:50  mshopf
  32.  * added revbump compatible version string.
  33.  * fixed ftp offline proxyproxy std port bug.
  34.  *
  35.  * Revision 0.6  1995/11/04  11:26:13  mshopf
  36.  * better shutdown (requeueing of current transmissions). small bug fixes.
  37.  *
  38.  * Revision 0.5  1995/11/02  18:26:13  mshopf
  39.  * queueing system implemented.
  40.  *
  41.  * Revision 0.4  1995/10/21  21:28:37  mshopf
  42.  * small bug fix.
  43.  *
  44.  * Revision 0.3  1995/10/17  19:23:09  mshopf
  45.  * cleaned up messy logging system.
  46.  * new option 'log'.
  47.  *
  48.  * Revision 0.2  1995/10/13  18:09:17  mshopf
  49.  * everything works so far, exceptions are noted in the header.
  50.  * :-)
  51.  *
  52.  * Revision 0.1  1995/10/13  10:44:27  mshopf
  53.  * no caching at all right now, but it works fine as a proxyproxy.
  54.  *
  55.  */
  56.  
  57.  
  58. /*)) */
  59. /*(( "Includes" */
  60.  
  61. #include "httpproxy_rev.h"
  62. #include <stdio.h>
  63. #include <stdlib.h>
  64. #include <stddef.h>
  65. #include <errno.h>
  66. #include <string.h>
  67. #include <unistd.h>
  68. #include <assert.h>
  69. #include <time.h>
  70. #include <ctype.h>
  71.  
  72. #include <syslog.h>
  73. #include <dirent.h>
  74. #include <sys/stat.h>
  75. #include <sys/syslog.h>
  76. #include <sys/types.h>
  77. #include <sys/time.h>
  78. #include <sys/ioctl.h>
  79. #include <sys/socket.h>
  80. #include <netdb.h>
  81. #include <netinet/in.h>
  82. #include <arpa/inet.h>
  83.  
  84. #ifdef _AMIGA
  85. #  include <bsdsocket.h>
  86. #  define ioctl IoctlSocket
  87. #  define ioctl_t long
  88. #  define strcasecmp stricmp
  89. #  ifdef sys_errlist
  90. #    define strerror(x) (sys_errlist [x])     /* Won't necessary for AmiTCP4.0 */
  91. #  endif
  92. #  define CURRENTDIR ""
  93. typedef long len_t;
  94. #else
  95. #  define ioctl_t int
  96. #  define CloseSocket close
  97. #  define CURRENTDIR "."
  98. typedef int len_t;
  99. #endif
  100.  
  101. #ifdef DEBUG
  102. #  define debug(x) printf x
  103. #else
  104. #  define debug(x) (0)
  105. /*#  define NDEBUG*/
  106. #endif
  107.  
  108. #include <assert.h>
  109.  
  110. /*)) */
  111. /*(( "Types / Variables / Constants" */
  112.  
  113. /* Types and constants */
  114.  
  115. #define CHECK_ADDRESS     FALSE   /* set this to TRUE, when you want to look up all addresses
  116.                    * (this is only important in proxyproxy mode, otherwise all
  117.                    * addresses need to be looked up anyway). If set to false,
  118.                    * it may be faster, but the proxy will not recognize a.b as a.b.c
  119.                    * and it cannot recognice alias names...
  120.                    * Note: Right now it is not wise to set this when you do not have
  121.                    * a personal name server, a name caching system or *big* hosts file. */
  122.  
  123. #define DEFAULT_PROXYPORT 8080
  124. #define DEFAULT_HTTPPORT  80
  125. #define DEFAULT_DELTIME  (48*60*60)    /* default: delete cached files on startup */
  126. #define DEFAULT_EXPIRETIME (24*60*60)  /* default: delete cache, when the page is requested and too old */
  127. #define DEFAULT_RELOADTIME 10          /* default: expire cache on reload inbetween */
  128.  
  129. #define MAX_REQUESTS     8        /* Maximum number of pending requests */
  130. #define MIN_REQUESTS     4        /* Minimum number of free requests for interactive actions */
  131. #define MAX_URLBUFFER    1024     /* currently no Url request may be larger than this value! */
  132. #define MAX_URLSAVE      128      /* maximum size of saved url requests */
  133. #define MAX_FILENAME     20       /* maximum size of filename */
  134. #define MAX_CACHES       1024     /* maximum number of cache slots */
  135. #define MAX_DATABUFFER   2048
  136. #define SHIFT_DATABUFFER 512      /* when x bytes are in the data buffer, shift it after send() */
  137.  
  138. #define REQ_REQSOCKET    0x01     /* a request socket is open */
  139. #define REQ_CONNSOCKET   0x02     /* a connection socket is open / to be connected */
  140. #define REQ_DONE         0x04     /* the connection socket is already closed */
  141. #define REQ_URLDONE      0x08     /* the URL is completely read (cache may be sent) */
  142. #define REQ_HTTP1X0      0x10     /* the URL was sent with HTTP/1.0 */
  143.  
  144. #define CACHE_VALID      0x01     /* Cache is filled and ready to serve */
  145. #define CACHE_QUEUED     0x02     /* Cache is already queued for regetting */
  146. #define CACHE_DELETETMP  0x04     /* There's a temporaray cache file to be removed */
  147.  
  148. #ifndef FALSE
  149. #define FALSE            (0)
  150. #define TRUE             (1)
  151. #endif
  152.                   /* Needed Memory */
  153. #define NEED_MEM         ((MAX_REQUESTS * sizeof (request_t) + MAX_CACHES * sizeof (cache_t)) / 1024)
  154.  
  155. #define difftime(x,y)    (((u_long)(x)) - ((u_long)(y)))
  156. #define isvalidhttp(x)   ((unsigned char)(x) > ' ' && (x) != '%' && (x) != '\\' && (unsigned char)(x) < 128)
  157.  
  158. /*
  159.  * Some typical Flag combinations in typical order:
  160.  * None:                           Empty slot
  161.  * REQ_REQSOCKET:                  Awaiting URL
  162.  * REQ_REQSOCKET | REQ_CONNSOCKET: Still geting URL, but 1. line is already there
  163.  *                                   *or*  URL done
  164.  *                                 Awaiting connection to remote host
  165.  *                                   *or*  already transfering data
  166.  * REQ_REQSOCKET | REQ_DONE:       The connection socket is already closed, but data is
  167.  *                                   still to be delivered, or data is sent from the cache
  168.  * REQ_CONNSOCKET:                 Request was terminated, still geting data to fill up
  169.  *                                   the cache (TIMEOUT needed!!!)
  170.  */
  171.  
  172. /* REQ_REQSOCKET: alone will never occour on ProxyProxy==TRUE... */
  173.  
  174.  
  175. typedef struct {
  176.            int  Flags;
  177.            char File [MAX_FILENAME];        /* "" if free slot / with begining '_' (data) */
  178.            char Url  [MAX_URLSAVE];         /* "" if free slot or just getting data */
  179.            } cache_t;
  180.  
  181. typedef struct {
  182.            long  ReqSocket, ConnSocket;
  183.            int   Flags;
  184.            char  UrlBuffer  [MAX_URLBUFFER];
  185.            char  DataBuffer [MAX_DATABUFFER];
  186.            int   UrlSent, UrlRecv;
  187.            int   DataSent, DataRecv;
  188.            cache_t *Cache;                  /* NULL if not cacheable */
  189.            FILE  *Stream;                   /* != NULL on open data transfer to/from cache */
  190.            } request_t;
  191.  
  192.  
  193. /* Global variables */
  194.  
  195. char      *VVersion= VERSTAG;
  196. char      *Version = VSTRING;
  197. FILE      *LogStream;
  198. char      *PrgName;
  199. long      ServerSocket;
  200. int       StdHttpPort;
  201. int       ServerPort    = DEFAULT_PROXYPORT;
  202. int       ProxyProxy    = FALSE;                    /* TRUE, when all requests should be
  203.                              * forwarded to another proxy. */
  204. struct sockaddr_in ProxyProxyIn;
  205.  
  206. cache_t   *LastCache    = NULL;
  207. time_t    ThisRequestTime = 0;
  208. time_t    StartUpTime;
  209. int       RequestsFree  = MAX_REQUESTS;
  210. int       CachesFree    = MAX_CACHES;
  211. long      CacheNr       = 0;
  212. u_long    DelCacheTime  = DEFAULT_DELTIME;
  213. u_long    ExpireCacheTime = DEFAULT_EXPIRETIME;
  214. u_long    ReloadCacheTime = DEFAULT_RELOADTIME;
  215. int       OffLine = 0;                              /* 0: get cache files again, when they are too old */
  216.                             /* 1: keep cache files and queue them */
  217. int       GetQueued = 0;                            /* 1: get queued data from remote hosts or proxyproxy */
  218. int       CacheUnreadRequests = FALSE;              /* 1: keep cache data on data connection close with data in url send buffer */
  219. request_t *Requests;
  220. cache_t   *Caches;
  221.  
  222.  
  223. void DeleteConnect (request_t *Req, int ok);
  224. void SaveCacheUrl (cache_t *c);
  225.  
  226. /*)) */
  227.  
  228. /*(( "Init ()" */
  229.  
  230. /* Init all global variables, open server port */
  231.  
  232. void Init (char *ProxyProxyHost, int ProxyProxyPort, char *LogName)
  233. {
  234.     struct servent  *ServEnt;
  235.     struct sockaddr_in SockIn;
  236.     struct hostent  *HostEnt;
  237.     ioctl_t on = 1;
  238.  
  239.     /* Standard setups */
  240.  
  241.     if (! (LogStream = fopen (LogName, "a+")) )
  242.     {
  243.     fprintf (stderr, "cannot open logfile '%s': %s\n", LogName, strerror (errno));
  244.     exit (20);
  245.     }
  246.  
  247.     if (! (Requests = calloc (MAX_REQUESTS, sizeof (request_t))) )
  248.     {
  249.     fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
  250.     exit (20);
  251.     }
  252.  
  253.     if (! (Caches = calloc (MAX_CACHES, sizeof (cache_t))) )
  254.     {
  255.     fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
  256.     exit (20);
  257.     }
  258.  
  259.     ThisRequestTime = StartUpTime = time (NULL);
  260.  
  261.     /* Get standard HTTP port (80 at the time of development...but you never know) */
  262.  
  263.     if (! (ServEnt = getservbyname ("http", "tcp")) )
  264.     {
  265.     StdHttpPort = DEFAULT_HTTPPORT;
  266.     syslog (LOG_WARNING, "%s: unknown protocol 'http', using default port %d", PrgName, DEFAULT_HTTPPORT);
  267.     }
  268.     else
  269.     StdHttpPort = ServEnt->s_port;
  270.  
  271.     /* Create Serversocket */
  272.  
  273.     if ( (ServerSocket = socket (PF_INET, SOCK_STREAM, 0)) < 0)
  274.     {
  275.     fprintf (stderr, "socket() for serverport failed: %s\n", strerror (errno));
  276.     exit (20);
  277.     }
  278.  
  279.     memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
  280.     SockIn.sin_family      = AF_INET;
  281.     SockIn.sin_addr.s_addr = INADDR_ANY;
  282.     SockIn.sin_port        = htons (ServerPort);
  283.     if (bind (ServerSocket, (struct sockaddr *) &SockIn, sizeof (struct sockaddr_in)) < 0)
  284.     {
  285.     fprintf (stderr, "bind() failed for port %d: %s\n", ServerPort, strerror (errno));
  286.     exit (20);
  287.     }
  288.  
  289. #ifdef FIOASYNC
  290.     if (ioctl (ServerSocket, FIOASYNC, (caddr_t) &on) < 0)
  291.     {
  292.     fprintf (stderr, "ioctl() failed for FIOASYNC: %s\n", strerror (errno));
  293.     exit (20);
  294.     }
  295. #endif
  296. #ifdef FIONBIO
  297.     if (ioctl (ServerSocket, FIONBIO, (caddr_t) &on) < 0)
  298.     {
  299.     fprintf (stderr, "ioctl() failed for FIONBIO: %s\n", strerror (errno));
  300.     exit (20);
  301.     }
  302. #endif
  303.  
  304.     if (listen (ServerSocket, 4) < 0)
  305.     {
  306.     fprintf (stderr, "listen() failed: %s\n", strerror (errno));
  307.     exit (20);
  308.     }
  309.  
  310.     /* Get ProxyProxy address */
  311.  
  312.     if (ProxyProxyHost)
  313.     {
  314.     ProxyProxy = TRUE;
  315.  
  316.     if (! OffLine)
  317.     {
  318.         memset ((char *) &ProxyProxyIn, 0, sizeof (struct sockaddr_in));
  319.         ProxyProxyIn.sin_family     = AF_INET;
  320.         ProxyProxyIn.sin_port       = htons (ProxyProxyPort);
  321.  
  322.         if ( (ProxyProxyIn.sin_addr.s_addr = inet_addr (ProxyProxyHost)) +0 == -1)                          /* !!! */
  323.         {
  324.         if (! (HostEnt = gethostbyname (ProxyProxyHost)) )
  325.         {
  326.             fprintf (stderr, "can't get proxyproxy host '%s'\n", ProxyProxyHost);
  327.             exit (20);
  328.         }
  329.         else
  330.             ProxyProxyIn.sin_addr.s_addr = ((struct in_addr *)(HostEnt->h_addr)) -> s_addr;
  331.         }
  332.         fprintf (LogStream, "%sstarting with proxyproxy host '%s', port %d\n",
  333.              Version, ProxyProxyHost, ProxyProxyPort);
  334.         debug (("%sstarting with proxyproxy host '%s', port %d\n",
  335.             Version, ProxyProxyHost, ProxyProxyPort));
  336.         return;
  337.     }
  338.     }
  339.     else if (! OffLine)
  340.     {
  341.     fprintf (LogStream, "%sstarting in normal mode\n", Version);
  342.     debug (("%sstarting in normal mode\n", Version));
  343.     return;
  344.     }
  345.  
  346.     fprintf (LogStream, "%sstarting in offline mode\n", Version);
  347.     debug  (("%sstarting in offline mode\n", Version));
  348. }
  349.  
  350.  
  351. /*)) */
  352. /*(( "InitCacheSlot()/GetFreeCacheSlot()/ErrToReq()" */
  353.  
  354. /* Initialize cache slot */
  355.  
  356. void InitCacheSlot (cache_t *c, cache_t *Template, char Init, char Separator)
  357. {
  358.     if (! Template)
  359.     {
  360.     c->Flags  = 0;
  361.     c->Url[0] = '\0';
  362.     sprintf (c->File, "%c%08lx%c%08lx", Init, StartUpTime, Separator, CacheNr++);
  363.     debug (("new cache allocated: '%s'\n", c->File));
  364.     }
  365.     else
  366.     {
  367.     c->Flags  = Template->Flags & ~CACHE_VALID;
  368.     strcpy  (c->Url, Template->Url);
  369.     sprintf (c->File, "%c%8.8s%c%8.8s", Init, & Template->File [1], Separator, & Template->File [10]);
  370.     debug (("cache %s from template %s\n", c->File, Template->File));
  371.     }
  372. }
  373.  
  374.  
  375. /* Scan for a free cache slot */
  376.  
  377. cache_t *GetFreeCacheSlot (void)
  378. {
  379.     cache_t *c;
  380.     int i;
  381.  
  382.     debug (("new cache requested.\n"));
  383.     if (! CachesFree)
  384.     return (NULL);
  385.  
  386.     for (c = Caches, i=0; i < MAX_CACHES; c++, i++)
  387.     if (c->Url[0] == '\0' && c->File[0] == '\0')
  388.     {
  389.         CachesFree--;
  390.         InitCacheSlot (c, NULL, '_', '.');
  391.         return (c);
  392.     }
  393.     assert (0);      /*NOTREACHED*/
  394. }
  395.  
  396.  
  397. /* Type an error to a data buffer of a pending request and set everything up.
  398.  * The error is printed into the Logstream, too. When Short == NULL, a information
  399.  * message is sent, and nothing is printed into the Logstream. */
  400.  
  401. void   ErrToReq (request_t *Req, int Number, char *Short, char *Descr)
  402. {
  403.     static char *Author  = "<A HREF=\"http://wwwcip.informatik.uni-erlangen.de/user/mshopf\">Matthias Hopf</A>";
  404.     Req->DataSent = 0;
  405.     if (Short)
  406.     {
  407.     fprintf (LogStream, "#%02d: Error: %s\n", (int) Req->ReqSocket, Short);
  408.     debug (("#%02d: Error: %s\n", (int) Req->ReqSocket, Short));
  409.  
  410.     sprintf (Req->DataBuffer, "HTTP/1.0 %03d %s\015\n"
  411.                   "Server: Httpproxy_V%d.%d\015\n"
  412.                   "Content-Type: text/html\015\n\015\n"
  413.                   "<HTML><H1>Proxy Error: %s</H1><P>\n"
  414.                   "%s<P>\n"
  415.                   "<HR><ADDRESS>%s by %s</ADDRESS>\n",
  416.                   Number, Short, VERSION, REVISION, Short, Descr, Version, Author);
  417.     }
  418.     else
  419.     sprintf (Req->DataBuffer, "HTTP/1.0 %03d Proxy Message\015\n"
  420.                   "Server: Httpproxy_V%d.%d\015\n"
  421.                   "Content-Type: text/html\015\n\015\n"
  422.                   "<HTML><H3>Proxy Message:</H3><P>\n"
  423.                   "%s<P>\n"
  424.                   "<HR><ADDRESS>%s by %s</ADDRESS>\n",
  425.                   Number, VERSION, REVISION, Descr, Version, Author);
  426.  
  427.     Req->DataRecv = strlen (Req->DataBuffer);
  428.     Req->Flags |= REQ_DONE;
  429.     if (Req->Flags & REQ_CONNSOCKET)
  430.     DeleteConnect (Req, FALSE);
  431. }
  432.  
  433.  
  434. /*)) */
  435. /*(( "RemCacheEntry()/CheckCacheTime()" */
  436.  
  437. /* Remove a cache entry and its according files */
  438.  
  439. void RemCacheEntry (cache_t *c)
  440. {
  441.     cache_t tmp;
  442.  
  443.     debug (("deleting cache file '%s', additional url: %s\n", c->File, c->Url[0] ? "yes" : "no"));
  444.     if (c->File[0] && c->File[0] != '.')                     /* It's no special message file */
  445.     {
  446.     if (remove (c->File))
  447.     {
  448.         fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
  449.         debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
  450.     }
  451.     if (c->Url[0])
  452.     {
  453.         c->File[0] = '@';
  454.         if (remove (c->File))
  455.         {
  456.         fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
  457.         debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
  458.         }
  459.     }
  460.     if (c->Flags & CACHE_DELETETMP)
  461.     {
  462.         InitCacheSlot (&tmp, c, '@', '@');
  463.         debug (("removing temporary url file '%s'\n", tmp.File));
  464.         if (remove (tmp.File))
  465.         {
  466.         fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
  467.         debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
  468.         }
  469.     }
  470.     }
  471.  
  472.     c->File[0] = c->Url[0] = '\0';
  473.     c->Flags = 0;
  474.     CachesFree++;
  475. }
  476.  
  477.  
  478. /* Check the modification time and date of a cache entry; set Time to 0 to force expire. */
  479. /* Returns -1, when the cache entry is expired or to be reloaded or not existing, the entry is removed.
  480.  * Returns -2, when the cache entry is expired and queued. */
  481.  
  482. int CheckCacheTime (cache_t *c, u_long Time)
  483. {
  484.     time_t FileT = -1;
  485.     struct stat s;
  486.  
  487.     if (stat (c->File, &s) >= 0)
  488.     {
  489.     FileT = s.st_mtime;
  490.     debug (("local 0x%lx, access 0x%lx, diff1 %ld <?> %s\n", ThisRequestTime, FileT,
  491.         difftime (ThisRequestTime, FileT), difftime (ThisRequestTime, FileT) > Time ? "expired" : "valid"));
  492.     if ((! Time) || difftime (ThisRequestTime, FileT) > Time)
  493.     {
  494.         if (OffLine)
  495.         {
  496.         if (! (c->Flags & CACHE_QUEUED))
  497.         {
  498.             cache_t tmp;
  499.             c->Flags |= CACHE_QUEUED;
  500.             assert  (c->Flags & CACHE_VALID);
  501.             InitCacheSlot (&tmp, c, '@', '@'); /* Queue Url for cache entry and return -2 */
  502.             SaveCacheUrl (&tmp);
  503.         }
  504.         return (-2);
  505.         }
  506.         else if (c->Flags & CACHE_QUEUED)       /* Is the entry queued and not yet sent? */
  507.         return (-2);
  508.         else
  509.         FileT = -1;
  510.     }
  511.     }
  512.  
  513.     if (FileT == -1)
  514.     {
  515.     if (c->Flags & CACHE_QUEUED)
  516.     {
  517.         debug (("cache entry '%s' not yet there (queued) - This should not happen...\n", c->File));
  518.         fprintf (stderr, "%s: Warning! Data consistency failure, line %d\n", PrgName, __LINE__);
  519.         return (-2);
  520.     }
  521.     else
  522.     {
  523.         debug (("cache entry '%s' expired / to be reloaded, removing\n", c->File));
  524.         RemCacheEntry (c);
  525.         return (-1);
  526.     }
  527.     }
  528.     return (c->Flags & CACHE_QUEUED ? -2 : 0);
  529. }
  530.  
  531. /*)) */
  532. /*(( "ReadCacheUrl()/SaveCacheUrl()" */
  533.  
  534. /* Read the Url from a specific url file */
  535.  
  536. void ReadCacheUrl (cache_t *c, char *Name)
  537. {
  538.     FILE *f;
  539.  
  540.     c->Url[0] = ' ';              /* RemCacheEntry shall remove the url file, too, in case it is called */
  541.     switch (CheckCacheTime (c, DelCacheTime)) {
  542.     case 0:
  543.     case -3:
  544.     break;
  545.  
  546.     case -1:
  547.     return;
  548.     default:
  549.     RemCacheEntry (c);
  550.     return;
  551.     }
  552.  
  553.     if (! (f = fopen (Name, "r")) )
  554.     {
  555.     fprintf (LogStream, "cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno));
  556.     debug (("cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno)));
  557.     RemCacheEntry (c);
  558.     return;
  559.     }
  560.     if (fgets (c->Url, MAX_URLSAVE, f))
  561.     {
  562.     switch (c->Url [strlen (c->Url) - 1]) {
  563.     case '\n':
  564.     case '\015':
  565.         break;
  566.  
  567.     default:                      /* Cache entry is valid -> return */
  568.         fclose (f);
  569.         c->Flags = CACHE_VALID;
  570.         debug (("valid cache entry: url '%s', data '%s'\n", c->Url, c->File));
  571.         return;
  572.     }
  573.     }
  574.     fclose (f);
  575.     fprintf (LogStream, "corrupt cache entry for url file '%s', removing\n", Name);
  576.     debug (("corrupt cache entry for url file '%s', removing\n", Name));
  577.     RemCacheEntry (c);
  578. }
  579.  
  580.  
  581. /* Save the cache's Url to its url file */
  582.  
  583. void SaveCacheUrl (cache_t *c)
  584. {
  585.     FILE *UrlStream;
  586.     cache_t tmp;
  587.  
  588.     c->File[0] = '@';
  589.     debug (("writing url file '%s'\n", c->File));
  590.     if ( (UrlStream = fopen (c->File, "w")) )
  591.     {
  592.     fprintf (UrlStream, "%s", c->Url);  /* no error checking - well... */
  593.     fclose (UrlStream);
  594.     c->File[0] = '_';
  595.     if (! (c->Flags & CACHE_QUEUED))
  596.         c->Flags |= CACHE_VALID;
  597.         /* fprintf (LogStream, "URL '%s' is now cached\n",
  598.          * c->Url);*/
  599.     if (c->Flags & CACHE_DELETETMP)
  600.     {
  601.         InitCacheSlot (&tmp, c, '@', '@');
  602.         debug (("removing temporary url file '%s'\n", tmp.File));
  603.         if (remove (tmp.File))
  604.         {
  605.         fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
  606.         debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
  607.         }
  608.         c->Flags &= ~CACHE_DELETETMP;
  609.     }
  610.     }
  611.     else
  612.     {
  613.     fprintf (LogStream, "cannot write cache url file '%s': %s\n",
  614.          c->File, strerror (errno));
  615.     debug (("cannot write cache url file '%s': %s\n",
  616.         c->File, strerror (errno)));
  617.     c->File[0] = '_';
  618.     if (c->Flags & CACHE_QUEUED)
  619.         c->File[0] = '\0';
  620.     RemCacheEntry (c);
  621.     }
  622. }
  623.  
  624.  
  625. /*)) */
  626. /*(( "BuildCache ()" */
  627.  
  628. /* Scan cache directory, build up cache and delete invalid cache entries */
  629.  
  630. void BuildCache (void)
  631. {
  632.     DIR *Dir;
  633.     cache_t *c = Caches, *cc, *ccc;
  634.     struct dirent *d;
  635.  
  636.     if (! (Dir = opendir (CURRENTDIR)))
  637.     {
  638.     fprintf (stderr, "%s: cannot open cache directory: %s\n", PrgName, strerror (errno));
  639.     exit (20);
  640.     }
  641.  
  642.     errno = 0;
  643.     while ( (d = readdir (Dir)) )
  644.     {
  645.     if (strlen (d->d_name) > MAX_FILENAME-1)
  646.     {
  647.         if (d->d_name[0] != '.')                 /* no special file... */
  648.         fprintf (stderr, "unknown file '%s' in cache directory\n", d->d_name);
  649.     }
  650.     else
  651.     {
  652.         strcpy (c->File, d->d_name);
  653.         if (c->File [0] == '_')                  /* found cache data - look for cache url */
  654.         {
  655.             debug (("found data '%s'\n", c->File));
  656.         for (cc = Caches; cc != c; cc++)
  657.             if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '@')
  658.             {
  659.             cc->File[0] = '_';
  660.             c->File[0]  = '@';
  661.             ReadCacheUrl (cc, c->File);  /* already read... */
  662.             c->File[0] = '\0';
  663.             break;
  664.             }
  665.         if (c == cc)                         /* not found... */
  666.         {
  667.             c++;                             /* keep it, may be we'll get the url file, too */
  668.             if (! --CachesFree)
  669.             {
  670.             fprintf (LogStream, "Cache table full while recacheing\n");
  671.             debug (("Cache table full while recacheing\n"));
  672.             break;                       /* break from while - no room left... */
  673.             }
  674.         }
  675.         }
  676.         else if (c->File [0] == '@')             /* found cache url - look for cache data */
  677.         {
  678.             debug (("found url '%s'\n", c->File));
  679.         for (cc = Caches; cc != c; cc++)
  680.             if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '_')
  681.             {
  682.             ReadCacheUrl (cc, c->File);  /* already read... */
  683.             c->File[0] = '\0';
  684.             break;
  685.             }
  686.         if (c == cc)                         /* not found... */
  687.         {
  688.             c++;                             /* keep it, may be we'll get the data file, too */
  689.             if (! --CachesFree)              /* otherwise it is a queued entry */
  690.             {
  691.             fprintf (LogStream, "Cache table full while recacheing\n");
  692.             debug (("Cache table full while recacheing\n"));
  693.             break;                       /* break from while - no room left... */
  694.             }
  695.         }
  696.         }
  697.         else
  698.         {
  699.         if (c->File [0] != '.')             /* .files, .httpproxy-log or standard directories (unix) */
  700.             fprintf (stderr, "unknown file '%s' in cache directory\n", c->File);
  701.         c->File[0] = '\0';
  702.         }
  703.     }
  704.     }
  705.     if (errno)
  706.     fprintf (stderr, "directory error while building cache table, continueing: %s\n", strerror (errno));
  707.     closedir (Dir);
  708.  
  709.     /* now remove all incomplete cachentries (and files) */
  710.     for (cc = Caches; cc != c; cc++)
  711.     if (cc->Url[0] == '\0' && cc->File [0] == '_')
  712.     {
  713.         fprintf (LogStream, "cache file '%s' invalid, removing.\n", cc->File);
  714.         debug (("cache file '%s' invalid, removing.\n", cc->File));
  715.         RemCacheEntry (cc);
  716.     }
  717.  
  718.     /* mark all url only caches as queued */
  719.     for (cc = Caches; cc != c; cc++)
  720.     if (cc->File [0] == '@')
  721.     {
  722.         ReadCacheUrl (cc, cc->File);             /* the file name remains '@...' */
  723.         if (cc->File [0] == '@')
  724.         {
  725.         cc->Flags = CACHE_QUEUED;            /* and ! CACHE_VAILD */
  726.         for (ccc = Caches; ccc != c; ccc++)
  727.             if (ccc->File [0] == '_')
  728.             if (cc != ccc && strcmp (ccc->Url, cc->Url) == 0)
  729.             {
  730.                 debug (("url '%s': queued url file %s associated with expired cache entry %s\n", cc->Url, cc->File, ccc->File));
  731.                 assert (ccc->Flags & CACHE_VALID);
  732.                 assert (cc->File [9] == '@');           /* it has to be a special queued url file... */
  733.  
  734.                 ccc->Flags |= CACHE_QUEUED | CACHE_DELETETMP;
  735.                 cc->File[0] = '\0';
  736.                 RemCacheEntry (cc);
  737.                 break;
  738.             }
  739.         if (ccc == c)
  740.             debug (("queued url '%s'\n", cc->Url));
  741.         }
  742.     }
  743. }
  744.  
  745.  
  746. /*)) */
  747. /*(( "ScanCache()" */
  748.  
  749. /* Scan whether a specific cache is already there, filled and ready to serve */
  750. /* Returns 0: no, not there  1: yes, all set up  -1: Failure
  751.  * 2: error message in buffer */
  752. /* Sets up the request struct for correct filling, opens all streams, etc. */
  753.  
  754. int ScanCache (request_t *Req, char *Url, int Known)
  755. {
  756.     int i;
  757.     cache_t *c;
  758.     static time_t LastRequestTime;
  759.  
  760.     debug (("Searching for cache for Url '%s'\n", Url));
  761.     LastRequestTime = ThisRequestTime;
  762.     ThisRequestTime = time (NULL);
  763.     debug (("Last: %ld, This: %ld, Diff: %ld, reload: %s\n", LastRequestTime, ThisRequestTime,
  764.         difftime (ThisRequestTime, LastRequestTime),
  765.         difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime ? "yes" : "no"));
  766.  
  767.     for (c = Caches, i=0; i < MAX_CACHES; c++, i++)
  768.     if (c->Url[0])
  769.     {
  770.         if (strcmp (c->Url, Url) == 0)
  771.         {
  772.         if (c->Flags & CACHE_VALID)
  773.         {
  774.             switch (CheckCacheTime (c, ExpireCacheTime)) {
  775.             case 0:
  776.             if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  777.             {
  778.                 if (CheckCacheTime (c, 0) == -2)         /* force queueing / reloading */
  779.                 {
  780.                 debug (("queued url on request\n"));
  781.                 ErrToReq (Req, 203, NULL, "Your request for reloading the document is queued.<BR>\n"
  782.                       "You will get the new document next time you are online.<BR>\n"
  783.                       "An expired cache entry exists and can be viewed by immedeately reloading this document.");
  784.                 return (2);
  785.                 }
  786.                 break;                                   /* forcing reload of url */
  787.             }
  788.  
  789.             if (! (Req->Stream = fopen (c->File, "r")) )
  790.             {
  791.                 fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
  792.                 debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
  793.                 RemCacheEntry (c);
  794.             }
  795.             else
  796.             {
  797.                 debug (("Found cache entry, file '%s'\n", c->File));
  798.                 Req->Cache  = LastCache = c;             /* ok, found cache entry */
  799.                 Req->Flags |= REQ_DONE;
  800.                 return (1);
  801.             }
  802.             break;
  803.  
  804.                 /* case -1: break; */
  805.             case -2:
  806.             if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  807.             {
  808.                 debug (("Sending (invalid) cache file '%s' on request\n", c->File));
  809.                 if (! (Req->Stream = fopen (c->File, "r")) )
  810.                 {
  811.                 fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
  812.                 debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
  813.                 RemCacheEntry (c);
  814.                 break;
  815.                 }
  816.                 else
  817.                 {
  818.                 Req->Cache  = c;                         /* ok, send invalid cache entry */
  819.                 Req->Flags |= REQ_DONE;
  820.                 return (1);
  821.                 }
  822.             }
  823.             else
  824.             {
  825.                 debug (("Cache entry expired, queued\n"));
  826.                 ErrToReq (Req, 203, NULL, "Your request is queued.<BR>\n"
  827.                       "You will get the document next time you are online.<BR>\n"
  828.                       "An expired cache entry exists and can be viewed by immedeately reloading this document.");
  829.                 LastCache = c;
  830.                 return (2);
  831.             }
  832.             }
  833.         }
  834.         else                                           /* c->Flags & CACHE_VALID */
  835.         {
  836.             if (c->Flags & CACHE_QUEUED)
  837.             {
  838.             if (OffLine)
  839.             {
  840.                 if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  841.                 {
  842.                 ErrToReq (Req, 404, "Already queued", "Your request is already queued.<BR>\n"
  843.                       "You will get the document next time you are online.<BR>\n"
  844.                       "You tried to get an expired cache entry, but this document is not cached right now.");
  845.                 return (2);
  846.                 }
  847.                 else
  848.                 {
  849.                 debug (("Cache entry already queued\n"));
  850.                 ErrToReq (Req, 203, NULL, "Your request is already queued.<BR>\n"
  851.                       "You will get the document next time you are online.<BR>\n"
  852.                       "There is no expired cache entry for this document.");
  853.                 LastCache = c;
  854.                 return (2);
  855.                 }
  856.             }
  857.             else
  858.             {
  859.                 debug (("unqueueing and getting Cache entry\n"));
  860.                 c->Flags   = 0;
  861.                 c->File[0] = '_';
  862.                 LastCache  = c;
  863.                 goto HaveAlreadyCache;              /* sorry... */
  864.             }
  865.             }
  866.             else
  867.             {
  868.             debug (("Second request while getting URL\n"));
  869.             ErrToReq (Req, 204, NULL, "The same request was sent from another connection.\n"
  870.                   "May be you requested a queued URL which is just being received.<BR>\n"
  871.                   "Please try again in a few moments.");
  872.             LastCache = c;
  873.             return (2);
  874.             }
  875.         }
  876.         }
  877.     }
  878.  
  879.     if (! (Known || ProxyProxy || OffLine))      /* not cached, unknown and online without proxyproxy... don't know where to get it */
  880.     {
  881.     ErrToReq (Req, 404, "Unknown host", "You tried to get a document from an unknown host. Please check the URL.");
  882.     debug (("#%02d: unknown host: URL '%s'\n", (int) Req->ReqSocket, Url));
  883.     return (2);
  884.     }
  885.  
  886.  
  887.     if (! (c = GetFreeCacheSlot ()) )            /* not in cache so far */
  888.     {
  889.     if (OffLine)
  890.     {
  891.         ErrToReq (Req, 500, "Cache table full", "I'm sorry, but the cache table is full.<BR>"
  892.               "It is not possible to queue up your request.");
  893.         return (2);
  894.     }
  895.     else
  896.     {
  897.         fprintf (LogStream, "Cache table full\n");
  898.         debug (("Cache table full\n"));
  899.         return (0);                          /* no more free entries - just proxy it */
  900.     }
  901.     }
  902.  
  903.     if (OffLine)
  904.     {
  905.     ErrToReq (Req, 203, NULL, "Your new request is queued.<BR>\n"
  906.           "You will get the document next time you are online.");
  907.     debug (("Request queued.\n"));
  908.     strcpy (c->Url, Url);
  909.     c->Flags = CACHE_QUEUED;
  910.     LastCache = c;
  911.     SaveCacheUrl (c);
  912.     return (2);
  913.     }
  914.  
  915. HaveAlreadyCache:
  916.  
  917.     if (! (Req->Stream = fopen (c->File, "w")) )
  918.     {
  919.     fprintf (LogStream, "cannot open new cache file '%s': %s\n", c->File, strerror (errno));
  920.     debug (("cannot open new cache file '%s': %s\n", c->File, strerror (errno)));
  921.     c->File[0] = '\0';
  922.     RemCacheEntry (c);
  923.     return (0);                              /* just proxy it */
  924.     }
  925.  
  926.     debug (("New cache file '%s'\n", c->File));
  927.     strcpy (c->Url, Url);                        /* new cache entry */
  928.     c->Flags = 0;
  929.     Req->Cache = LastCache = c;
  930.     return (0);
  931. }
  932.  
  933.  
  934. /*)) */
  935. /*(( "GetCacheData ()" */
  936.  
  937. /* Fill request data space with data from cache
  938.  * when a read error occures, no data will be received and ServWrite()
  939.  * will automagically terminate the socket. */
  940.  
  941. void GetCacheData (request_t *Req)
  942. {
  943.     int Bytes;
  944.  
  945.     assert (Req->DataRecv < MAX_DATABUFFER);
  946.     assert (Req->Stream);
  947.  
  948.     debug (("Getting more cache data - Url done: %s\n", Req->Flags & REQ_URLDONE ? "yes" : "no"));
  949.  
  950.     if ( (Bytes = fread (& Req->DataBuffer [Req->DataRecv], sizeof (char),
  951.             MAX_DATABUFFER - Req->DataRecv, Req->Stream)) > 0)
  952.     Req->DataRecv += Bytes;
  953.     else
  954.     if (! feof (Req->Stream))
  955.     {
  956.     fprintf (LogStream, "read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno));
  957.     debug (("read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno)));
  958.     }
  959.     debug (("Read %d bytes\n", Bytes));
  960. }
  961.  
  962.  
  963. /*)) */
  964. /*(( "BuildFdSets ()" */
  965.  
  966. /* Build fdsets (outstanding reads and writes) */
  967.  
  968. void BuildFdSets (fd_set *ReadSet, fd_set *WriteSet)
  969. {
  970.     int i;
  971.     request_t *Req;
  972.  
  973.     FD_ZERO (ReadSet);
  974.     FD_ZERO (WriteSet);
  975.  
  976.     debug (("Build: "));
  977.     if (RequestsFree)
  978.     {
  979.     debug (("Server %d", (int) ServerSocket));
  980.     FD_SET  (ServerSocket, ReadSet);
  981.     }
  982.  
  983.     for (i=0, Req=Requests; i < MAX_REQUESTS; i++, Req++)
  984.     {
  985.     if (Req->Flags & REQ_REQSOCKET)
  986.     {
  987.         if (Req->UrlRecv < MAX_URLBUFFER-1)
  988.         {
  989.         debug ((", Read Req %d", (int) Req->ReqSocket));
  990.         FD_SET (Req->ReqSocket, ReadSet);
  991.         }
  992.         if ((Req->Flags & (REQ_URLDONE | REQ_DONE))
  993.         && Req->DataRecv > Req->DataSent)              /* only write when the request ist finished */
  994.         {
  995.         debug ((", Write Req %d", (int) Req->ReqSocket));
  996.         FD_SET (Req->ReqSocket, WriteSet);
  997.         }
  998.     }
  999.     if (Req->Flags & REQ_CONNSOCKET)
  1000.     {
  1001.         if (Req->UrlRecv > Req->UrlSent)                   /* Waiting for connect or sending request */
  1002.         {
  1003.         debug ((", Write Con %d", (int) Req->ConnSocket));
  1004.         FD_SET (Req->ConnSocket, WriteSet);
  1005.         }
  1006.         if (Req->DataRecv < MAX_DATABUFFER)                /* Transfer Data */
  1007.         {
  1008.         debug ((", Read Con %d", (int) Req->ConnSocket));
  1009.         FD_SET (Req->ConnSocket, ReadSet);
  1010.         }
  1011.     }
  1012.     }
  1013.     debug (("\n"));
  1014. }
  1015.  
  1016.  
  1017. /*)) */
  1018. /*(( "Scan/CheckUrl ()" */
  1019.  
  1020. /* Check whether the URL is read completely. Sets REQ_URLDONE accordingly.
  1021.  * Attention! This routine relys on the fact, that ScanUrl was called already!
  1022.  * That means especially, that the first line was already read completely. */
  1023.  
  1024. void CheckUrl (request_t *Req)
  1025. {
  1026.     debug (("Checking Url\n"));
  1027.     if (Req->Flags & REQ_HTTP1X0)
  1028.     {
  1029.     if (strstr (Req->UrlBuffer, "\n\n") || strstr (Req->UrlBuffer, "\015\015") ||
  1030.         strstr (Req->UrlBuffer, "\n\015\n\015") || strstr (Req->UrlBuffer, "\015\n\015\n"))
  1031.         Req->Flags |= REQ_URLDONE;
  1032.     }
  1033.     else
  1034.     Req->Flags |= REQ_URLDONE;
  1035. #ifdef DEBUG
  1036.    if (Req->Flags & REQ_URLDONE)
  1037.     debug (("http request complete\n"));
  1038. #endif
  1039. }
  1040.  
  1041.  
  1042. /* Scan the URL and divide it into several parts. Understands http/0.9
  1043.  * and http/1.0 versions right now. Sets REQ_URLDONE when URL is complete. */
  1044. /* Returns -1 in case of failure, 0 on correct termination or found cache,
  1045.  * 1: found cache slot, FileD is open, 2: failure, cache contains error */
  1046. /* Port and Address are value-return arguments. */
  1047.  
  1048. int ScanUrl (request_t *Req, struct in_addr *Address, int *Port)
  1049. {
  1050.     struct hostent *HostEnt;
  1051.     struct in_addr In;
  1052.     char BufGET [12];
  1053.     char BufPROT [16];
  1054.     char BufDOK [MAX_URLSAVE + 256];                                       /* a critical array... but that's enough */
  1055.     char BufURL [128];
  1056.     char BufVERS [8];
  1057.     int  CharsRead, CharsRead2, FoundHost = 0, i;
  1058.     char *DokPtr, *PortPtr;
  1059.     char *Host;
  1060.  
  1061.     if (sscanf (Req->UrlBuffer, "%11s %15[a-zA-Z0-9]%n", BufGET, BufPROT, &CharsRead) < 2)
  1062.     return (-1);
  1063.     debug (("GET '%s', PROT '%s'\n", BufGET, BufPROT));
  1064.     if (CharsRead >= Req->UrlRecv)
  1065.     return (-1);
  1066.     if (strcasecmp (BufGET, "get") != 0)                                   /* no get command */
  1067.     return (-1);
  1068.     if (! ProxyProxy)
  1069.     if (strcasecmp (BufPROT, "http") != 0)                             /* we only support http urls so far */
  1070.         return (-1);
  1071.     if (strncmp (& Req->UrlBuffer [CharsRead], "://", 3) != 0)
  1072.     return (-1);
  1073.     if (sscanf (& Req->UrlBuffer [CharsRead+3], "%126s%n", BufURL, &CharsRead2) < 1)
  1074.     return (-1);
  1075.     debug (("URL '%s'\n", BufURL));
  1076.     CharsRead += 3 + CharsRead2;
  1077.  
  1078.     if (! (DokPtr = strchr (BufURL, '/')) )                                /* no host... */
  1079.     return (-1);
  1080.     *DokPtr++ = 0;
  1081.  
  1082.     if (strcasecmp (BufPROT, "http") != 0)
  1083.     *Port = -1;
  1084.     else
  1085.     *Port = StdHttpPort;
  1086.     if ( (PortPtr = strchr (BufURL, ':')) )
  1087.     {
  1088.     *PortPtr = 0;
  1089.     *Port = atoi (PortPtr + 1);
  1090.     }
  1091.  
  1092.     Host = BufURL;
  1093. #if (CHECK_ADDRESS != TRUE)
  1094.     if (! (ProxyProxy || OffLine))
  1095.     {
  1096. #endif
  1097.     if ( (In.s_addr = inet_addr (BufURL)) != -1)
  1098.     {
  1099.         if ( (HostEnt = gethostbyaddr ((caddr_t) &In, sizeof (In), AF_INET)) )
  1100.         {
  1101.         debug (("gethostbyaddr () succeded\n"));
  1102.         Host = HostEnt->h_name;
  1103.         FoundHost = 1;
  1104.         }
  1105.         else
  1106.         Host = inet_ntoa (In);
  1107.     }
  1108.     else if ( (HostEnt = gethostbyname (BufURL)) )                     /* try to get unique name */
  1109.     {
  1110.         debug (("gethostbyname () succeded\n"));
  1111.         Host = HostEnt->h_name;
  1112.         In.s_addr = ((struct in_addr *)(HostEnt->h_addr)) -> s_addr;
  1113.         FoundHost = 1;
  1114.     }
  1115. #if (CHECK_ADDRESS != TRUE)
  1116.     }
  1117.     else
  1118.     FoundHost = 1;           /* we do know our proxy host */
  1119. #endif
  1120.  
  1121.     *Address = In;
  1122.  
  1123.     BufVERS [2] = 0;                                                       /* BufVERS is missused here... */
  1124.     if (*Port > -1)
  1125.     sprintf (BufDOK, "%s://%s:%d/", BufPROT, Host, *Port);
  1126.     else
  1127.     sprintf (BufDOK, "%s://%s/", BufPROT, Host);
  1128.     for (PortPtr = BufDOK + strlen (BufDOK); *DokPtr; DokPtr++, PortPtr++)
  1129.     {
  1130.     if (*DokPtr == '%')
  1131.     {
  1132.         if (! (BufVERS [0] = *++DokPtr) )
  1133.         break;
  1134.         if (! (BufVERS [1] = *++DokPtr) )
  1135.         break;
  1136.         if ( (i = strtol (BufVERS, NULL, 0x10)) )
  1137.         *PortPtr = i;
  1138.         else
  1139.         *PortPtr = '?';
  1140.     }
  1141.     else
  1142.         *PortPtr = *DokPtr;
  1143.     }
  1144.     *PortPtr = 0;
  1145.  
  1146.     debug (("Proto '%s', Host '%s', Port %d, Dok '%s'\n", BufPROT, Host, *Port, BufDOK));
  1147.  
  1148.     if (Req->UrlBuffer [CharsRead] == ' ' && CharsRead < Req->UrlRecv)
  1149.     if (sscanf (& Req->UrlBuffer [CharsRead], " %5s%*s\n", BufVERS) == 1)
  1150.         if (strcasecmp (BufVERS, "http/") == 0)
  1151.         {
  1152.         debug (("got HTTP/1.0 or greater request\n"));
  1153.         Req->Flags |= REQ_HTTP1X0;
  1154.         }
  1155.  
  1156.     CheckUrl (Req);
  1157.     if (strlen (BufDOK) > MAX_URLSAVE-1)                     /* That one cannot be cached */
  1158.     {
  1159.     fprintf (LogStream, "UrlBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK);
  1160.     debug (("UrlBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK));
  1161.     return (0);
  1162.     }
  1163.     return (ScanCache (Req, BufDOK, FoundHost));
  1164. }
  1165.  
  1166.  
  1167. /*)) */
  1168. /*(( "DeleteRequest/Connect ()" */
  1169.  
  1170. /* Close the connection socket only. When a request socket is still open
  1171.  * and no data is in the cache, close all.
  1172.  * Delete cache entry in case of an error. Check the answer whether it is
  1173.  * an error message or a regular answer. Errors should not be cached! */
  1174.  
  1175. void DeleteConnect (request_t *Req, int ok)
  1176. {
  1177. /*    cache_t tmp;*/
  1178.  
  1179.     debug (("DeleteCon %d\n", (int) Req->ConnSocket));
  1180.     if (Req->Flags & REQ_CONNSOCKET)
  1181.     {
  1182.     CloseSocket (Req->ConnSocket);
  1183.     Req->Flags &= ~REQ_CONNSOCKET;
  1184.     if (Req->Stream)
  1185.         fclose (Req->Stream);
  1186.     Req->Stream = NULL;
  1187.     if (ok)
  1188.     {
  1189.         if (Req->Cache && ! (Req->Cache->Flags & CACHE_QUEUED)) /* we've written into a cache file */
  1190.         {
  1191.         SaveCacheUrl (Req->Cache);
  1192. /*                if (Req->Cache->Flags & CACHE_DELETETMP)
  1193.         {
  1194.             InitCacheSlot (&tmp, Req->Cache, '@', '@');
  1195.             if (remove (tmp.File))
  1196.             {
  1197.             fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
  1198.             debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
  1199.             }
  1200.         }
  1201.         else
  1202.             SaveCacheUrl (Req->Cache);*/
  1203.         Req->Cache = NULL;
  1204.         }
  1205.     }
  1206.     else if (Req->Cache)                                     /* not ok - a error occured */
  1207.     {
  1208.         fprintf (LogStream, "error while receiving URL '%s' - removing\n", Req->Cache->Url);
  1209.         debug (("error while receiving URL '%s' - removing\n", Req->Cache->Url));
  1210.         Req->Cache->Url[0] = '\0';
  1211.         RemCacheEntry (Req->Cache);
  1212.         Req->Cache = NULL;
  1213.     }
  1214.     }
  1215.     if (Req->Flags & REQ_REQSOCKET)
  1216.     {
  1217.     if (Req->DataRecv <= Req->DataSent)
  1218.     {
  1219.         CloseSocket (Req->ReqSocket);       /* All done. Hugh! */
  1220.         Req->Flags = 0;
  1221.     }
  1222.     else
  1223.         Req->Flags |= REQ_DONE;
  1224.     }
  1225.     else
  1226.     Req->Flags = 0;
  1227.     if (! Req->Flags)
  1228.     RequestsFree++;
  1229. }
  1230.  
  1231.  
  1232. /* Close the request socket only. When a connection socket is already up
  1233.  * (and enough data is already read) continue filling up the cache. When
  1234.  * everything is done, close all. The URL should be checked, too.
  1235.  * When it is not complete, all connections should be terminated. */
  1236.  
  1237. void DeleteRequest (request_t *Req)
  1238. {
  1239.     debug (("DeleteReq %d\n", (int) Req->ReqSocket));
  1240.     if (Req->Flags & REQ_REQSOCKET)
  1241.     {
  1242.     CloseSocket (Req->ReqSocket);
  1243.     Req->Flags &= ~REQ_REQSOCKET;
  1244.     }
  1245.  
  1246.     if (Req->Flags & REQ_DONE)                  /* Maybe we're getting data from the cache */
  1247.     {
  1248.     if (Req->Stream)
  1249.         fclose (Req->Stream);
  1250.     Req->Stream = NULL;
  1251.     Req->Flags  = 0;                        /* All done. Hugh! */
  1252.     debug (("All done - deleterequest\n"));
  1253.     DeleteConnect (Req, FALSE);
  1254.     }
  1255.     else if (Req->Flags & REQ_URLDONE)
  1256.     {
  1257.     if (! (Req->Flags & REQ_CONNSOCKET))    /* Aborting cache sending */
  1258.         DeleteConnect (Req, FALSE);
  1259.     else
  1260.         Req->DataSent = Req->DataRecv = 0;  /* Continue getting data into the cache */
  1261.     }
  1262.     else
  1263.     DeleteConnect (Req, FALSE);             /* No request known so far... */
  1264. }
  1265.  
  1266.  
  1267. /*)) */
  1268. /*(( "ServConnect ()" */
  1269.  
  1270. /* The first line of the URL is already there, so we can connect to the
  1271.  * remote host. */
  1272.  
  1273. void ServConnect (request_t *Req)
  1274. {
  1275.     struct sockaddr_in SockIn;
  1276.     struct sockaddr_in *SockPtr = &SockIn;
  1277.     ioctl_t on = 1;
  1278.     int  Port;
  1279.  
  1280.     debug (("ServCon for Req %d; Flags %x\n", (int) Req->ReqSocket, Req->Flags));
  1281.  
  1282.     if (Req->Flags & REQ_REQSOCKET)
  1283.     {
  1284.     if (strchr (Req->UrlBuffer, '\n') == 0 &&
  1285.         strchr (Req->UrlBuffer, '\015') == 0)         /* first line of URL is not there... */
  1286.         return;
  1287.  
  1288.     if (Req->Flags & (REQ_CONNSOCKET | REQ_DONE))     /* already set up */
  1289.     {
  1290.         CheckUrl (Req);
  1291.         return;
  1292.     }
  1293.     }
  1294.  
  1295.     memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
  1296.  
  1297.     switch (ScanUrl (Req, &SockIn.sin_addr, &Port)) {
  1298.     case 0:                                   /* need to get document from remote host */
  1299.     if (Req->Cache)
  1300.     {
  1301.         fprintf (LogStream, "#%02d: getting URL '%s'\n", (int) Req->ReqSocket, Req->Cache->Url);
  1302.         debug (("#%02d: getting URL '%s'\n", (int) Req->ReqSocket, Req->Cache->Url));
  1303.     }
  1304.     else
  1305.     {
  1306.         fprintf (LogStream, "#%02d: serving uncacheable request '%s'\n",
  1307.              (int) Req->ReqSocket, Req->UrlBuffer);
  1308.         debug  (("#%02d: serving uncacheable request '%s'\n",
  1309.              (int) Req->ReqSocket, Req->UrlBuffer));
  1310.     }
  1311.     break;
  1312.  
  1313.     case 1:                                   /* already cached - no need to connect to remote host */
  1314.     assert (Req->Cache != NULL);
  1315.     fprintf (LogStream, "#%02d: sending URL '%s' from cache '%s'\n",
  1316.          (int) Req->ReqSocket, Req->Cache->Url, Req->Cache->File);
  1317.     debug  (("#%02d: sending URL '%s' from cache '%s'\n",
  1318.          (int) Req->ReqSocket, Req->Cache->Url, Req->Cache->File));
  1319.     GetCacheData (Req);
  1320.     return;
  1321.  
  1322.     case 2:                                   /* send error message from buffer */
  1323.     return;
  1324.  
  1325.     default:
  1326.     {
  1327.         fprintf (LogStream, "#%02d: invalid request\n", (int) Req->ReqSocket);
  1328.         debug (("#%02d: invalid request\n", (int) Req->ReqSocket));
  1329.         DeleteRequest (Req);
  1330.         return;
  1331.     }
  1332.     }
  1333.  
  1334.     if (OffLine)
  1335.     {
  1336.     ErrToReq (Req, 500, "Unable to serv", "It is not possible to connect to a remote host in offline mode.<BR>\n"
  1337.           "Either the requested URL is to long to be queued or an internal error has occured. When the\n"
  1338.           "URL is rather short, please contact the author of httpproxy.");
  1339.     return;
  1340.     }
  1341.  
  1342.     if (ProxyProxy)
  1343.     SockPtr = &ProxyProxyIn;
  1344.     else
  1345.     {
  1346.     SockIn.sin_family     = AF_INET;
  1347.     SockIn.sin_port       = htons (Port);
  1348.     }
  1349.  
  1350.  
  1351.     if ( (Req->ConnSocket = socket (AF_INET, SOCK_STREAM, 0)) < 0)                 /* open connection */
  1352.     {
  1353.     syslog (LOG_ERR, "%s: socket () failed", PrgName);
  1354.     DeleteRequest (Req);
  1355.     return;
  1356.     }
  1357.  
  1358.     Req->Flags |= REQ_CONNSOCKET;
  1359.  
  1360. #ifdef FIOASYNC
  1361.     if (ioctl (Req->ConnSocket, FIOASYNC, (caddr_t) &on) < 0)
  1362.     {
  1363.     syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
  1364.     DeleteRequest (Req);
  1365.     return;
  1366.     }
  1367. #endif
  1368. #ifdef FIONBIO
  1369.     if (ioctl (Req->ConnSocket, FIONBIO, (caddr_t) &on) < 0)
  1370.     {
  1371.     syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
  1372.     DeleteRequest (Req);
  1373.     return;
  1374.     }
  1375. #endif
  1376.  
  1377.     if (connect (Req->ConnSocket, (struct sockaddr *) SockPtr, sizeof (struct sockaddr_in)) < 0)
  1378.     {
  1379.     if (errno != EINPROGRESS)
  1380.     {
  1381.         if (ProxyProxy)
  1382.         {
  1383.         fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
  1384.         debug (("#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1385.         }
  1386.         else
  1387.         {
  1388.         fprintf (LogStream, "#%02d: remote host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
  1389.         debug (("#%02d: remote host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1390.         }
  1391.         DeleteRequest (Req);
  1392.         return;
  1393.     }
  1394.     }
  1395.     debug (("ServCon -> Conn %d\n", (int) Req->ConnSocket));
  1396. }
  1397.  
  1398.  
  1399. /*)) */
  1400. /*(( "ServServer ()" */
  1401.  
  1402. /* Serv the server port. Accept new connections, set up request database */
  1403.  
  1404. void ServServer (void)
  1405. {
  1406.     struct hostent *Host;
  1407.     struct sockaddr_in PeerIn;
  1408.     len_t  Len = sizeof (struct sockaddr_in);
  1409.     request_t *Req = Requests;
  1410.     ioctl_t on = 1;
  1411.  
  1412.     debug (("accept Req %d\n", (int) Req->ReqSocket));
  1413.     assert (RequestsFree > 0);
  1414.  
  1415.     while (Req->Flags)                        /* any Flags set -> occupied */
  1416.     Req++;
  1417.     assert (Req - Requests < MAX_REQUESTS);
  1418.  
  1419.     if ( (Req->ReqSocket = accept (ServerSocket, (struct sockaddr *) &PeerIn, &Len)) < 0)
  1420.     {
  1421.     syslog (LOG_ERR, "%s: accept() failed: %s", PrgName, strerror (errno));
  1422.     return;
  1423.     }
  1424.  
  1425.     if ( (Host = gethostbyaddr ((caddr_t) &PeerIn, sizeof (PeerIn), AF_INET)) )
  1426.     {
  1427.     fprintf (LogStream, "#%02d: new request from %s:%d\n",
  1428.          (int) Req->ReqSocket, Host->h_name, PeerIn.sin_port);
  1429.     debug  (("#%02d: new request from %s:%d\n",
  1430.         (int) Req->ReqSocket, Host->h_name, PeerIn.sin_port));
  1431.     }
  1432.     else
  1433.     {
  1434.     fprintf (LogStream, "#%02d: new request from %s:%d\n",
  1435.          (int) Req->ReqSocket, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port);
  1436.     debug  (("#%02d: new request from %s:%d\n",
  1437.          (int) Req->ReqSocket, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port));
  1438.     }
  1439.  
  1440.     Req->Flags |= REQ_REQSOCKET;
  1441.     Req->UrlSent = Req->UrlRecv = Req->DataSent = Req->DataRecv = 0;
  1442.     Req->Cache = NULL;
  1443.     Req->Stream = NULL;
  1444.     RequestsFree--;
  1445.  
  1446. #ifdef FIOASYNC
  1447.     if (ioctl (Req->ReqSocket, FIOASYNC, (caddr_t) &on) < 0)
  1448.     {
  1449.     syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
  1450.     DeleteRequest (Req);
  1451.     return;
  1452.     }
  1453. #endif
  1454. #ifdef FIONBIO
  1455.     if (ioctl (Req->ReqSocket, FIONBIO, (caddr_t) &on) < 0)
  1456.     {
  1457.     syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
  1458.     DeleteRequest (Req);
  1459.     return;
  1460.     }
  1461. #endif
  1462. }
  1463.  
  1464.  
  1465. /*)) */
  1466. /*(( "ServRead ()" */
  1467.  
  1468. /* Server for all reads */
  1469.  
  1470. void ServRead (fd_set *ReadSet)
  1471. {
  1472.     int i, Bytes;
  1473.     register request_t *Req;
  1474.  
  1475.     if (FD_ISSET (ServerSocket, ReadSet))
  1476.     ServServer ();
  1477.     for (i=0, Req=Requests; i < MAX_REQUESTS; i++, Req++)
  1478.     {
  1479.     if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, ReadSet))
  1480.     {
  1481.         assert (Req->UrlRecv < MAX_URLBUFFER-1);
  1482.         if ( (Bytes = recv (Req->ReqSocket, & Req->UrlBuffer [Req->UrlRecv],
  1483.                 (long) MAX_URLBUFFER-1 - Req->UrlRecv, 0)) < 0)
  1484.         {
  1485.         fprintf (LogStream, "#%02d: on receiving URL: %s\n", (int) Req->ReqSocket, strerror (errno));
  1486.         debug (("#%02d: on receiving URL: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1487.         DeleteRequest (Req);
  1488.         }
  1489.         else
  1490.         {
  1491.         Req->UrlRecv += Bytes;
  1492.         Req->UrlBuffer [Req->UrlRecv] = '\0';        /* needed for strchr() */
  1493.  
  1494.         debug (("Read %d bytes from Req %d containing:\n'%s'\n", Bytes,
  1495.             (int) Req->ReqSocket, & Req->UrlBuffer [Req->UrlRecv - Bytes]));
  1496.         debug (("= 0x %08lx %08lx %08lx %08lx\n", * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes),
  1497.             * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes + 4), * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes + 8),
  1498.             * (long *) (Req->UrlBuffer + Req->UrlRecv - Bytes + 12)));
  1499.  
  1500.         if (Bytes == 0)                              /* request socket gone away */
  1501.             DeleteRequest (Req);
  1502.         ServConnect (Req);                           /* Check if anything can be done already */
  1503.         if ((Req->UrlRecv == MAX_URLBUFFER-1) && ! (Req->Flags & REQ_CONNSOCKET))
  1504.         {                                            /* buffer full and no host specification */
  1505.             fprintf (LogStream, "#%02d: URL buffer overflow\n", (int) Req->ReqSocket);
  1506.             debug (("#%02d: URL buffer overflow\n", (int) Req->ReqSocket));
  1507.             DeleteRequest (Req);
  1508.         }
  1509.         }
  1510.     }
  1511.     if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, ReadSet))
  1512.     {
  1513.         assert (Req->DataRecv < MAX_DATABUFFER);
  1514.         if ( (Bytes = recv (Req->ConnSocket, & Req->DataBuffer [Req->DataRecv],
  1515.                 (long) MAX_DATABUFFER - Req->DataRecv, 0)) < 0)
  1516.         {
  1517.         if (ProxyProxy)
  1518.         {
  1519.             fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
  1520.             debug (("#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1521.         }
  1522.         else
  1523.         {
  1524.             fprintf (LogStream, "#%02d: on receiving data: %s\n", (int) Req->ReqSocket, strerror (errno));
  1525.             debug (("#%02d: on receiving data: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1526.         }
  1527.         DeleteConnect (Req, CacheUnreadRequests);
  1528.         }
  1529.         else
  1530.         {
  1531.         debug (("Read %d bytes from Con %d\n", Bytes, (int) Req->ConnSocket));
  1532.         if (Bytes == 0)                               /* We're done */
  1533.             DeleteConnect (Req, TRUE);
  1534.         else if (Req->Stream)
  1535.             fwrite (& Req->DataBuffer [Req->DataRecv], sizeof (char),      /* no error checking... */
  1536.                 Bytes, Req->Stream);
  1537.         if (Req->Flags & REQ_REQSOCKET)
  1538.             Req->DataRecv += Bytes;                   /* else: only save cache data */
  1539.         }
  1540.     }
  1541.     }
  1542. }
  1543.  
  1544.  
  1545. /*)) */
  1546. /*(( "ServWrite ()" */
  1547.  
  1548. /* Server for all writes */
  1549.  
  1550. void ServWrite (fd_set *WriteSet)
  1551. {
  1552.     int i, Bytes;
  1553.     register request_t *Req;
  1554.  
  1555.     for (i=0, Req=Requests; i < MAX_REQUESTS; i++, Req++)
  1556.     {
  1557.     if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, WriteSet))
  1558.     {
  1559.         assert (Req->DataRecv > Req->DataSent);
  1560.         if ( (Bytes = send (Req->ReqSocket, & Req->DataBuffer [Req->DataSent],
  1561.                 (long) Req->DataRecv - Req->DataSent, 0)) < 0)
  1562.         {
  1563.         fprintf (LogStream, "#%02d: on sending data: %s\n", (int) Req->ReqSocket, strerror (errno));
  1564.         debug (("#%02d: on sending data: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1565.         DeleteRequest (Req);
  1566.         }
  1567.         else
  1568.         {
  1569.         debug (("Wrote %d bytes to Req %d\n", Bytes, (int) Req->ReqSocket));
  1570.         Req->DataSent += Bytes;
  1571.         assert (Req->DataRecv >= Req->DataSent);
  1572.         if (Bytes == 0)                            /* request socket is unable to receive data */
  1573.         {
  1574.             fprintf (LogStream, "#%02d: request socket unable to receive data\n", (int) Req->ReqSocket);
  1575.             debug (("#%02d: request socket unable to receive data\n", (int) Req->ReqSocket));
  1576.             DeleteRequest (Req);
  1577.         }
  1578.  
  1579.         if (Req->DataSent == Req->DataRecv)        /* clear / shift data buffer */
  1580.             Req->DataSent = Req->DataRecv = 0;
  1581.         else if (Req->DataSent > SHIFT_DATABUFFER)
  1582.         {
  1583.             debug (("shifting databuffer size %d by %d bytes\n", Req->DataRecv - Req->DataSent, Req->DataSent));
  1584.             memmove (Req->DataBuffer, & Req->DataBuffer [Req->DataSent],
  1585.                  Req->DataRecv - Req->DataSent);
  1586.             Req->DataRecv -= Req->DataSent;
  1587.             Req->DataSent = 0;
  1588.         }
  1589.         if (Req->DataRecv < MAX_DATABUFFER && (Req->Flags & REQ_DONE) && Req->Stream)
  1590.             GetCacheData (Req);
  1591.         if (Req->DataRecv == 0 && (Req->Flags & REQ_DONE))   /* We're already done */
  1592.             DeleteRequest (Req);
  1593.         }
  1594.     }
  1595.     if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, WriteSet))
  1596.     {
  1597.         assert (Req->UrlRecv > Req->UrlSent);
  1598.         if ( (Bytes = send (Req->ConnSocket, & Req->UrlBuffer [Req->UrlSent],
  1599.                 (long) Req->UrlRecv - Req->UrlSent, 0)) < 0)
  1600.         {
  1601.         if (ProxyProxy)
  1602.         {
  1603.             fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno));
  1604.             debug (("#%02d: proxyproxy host unreachable: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1605.         }
  1606.         else
  1607.         {
  1608.             fprintf (LogStream, "#%02d: on sending url: %s\n", (int) Req->ReqSocket, strerror (errno));
  1609.             debug (("#%02d: on sending url: %s\n", (int) Req->ReqSocket, strerror (errno)));
  1610.         }
  1611.         DeleteConnect (Req, FALSE);
  1612.         }
  1613.         else
  1614.         {
  1615.         debug (("Wrote %d bytes to Conn %d\n", Bytes, (int) Req->ConnSocket));
  1616.         Req->UrlSent += Bytes;
  1617.         if (Bytes == 0)                               /* connection socket is unable to receive url */
  1618.         {
  1619.             fprintf (LogStream, "#%02d: connection socket unable to receive url\n", (int) Req->ReqSocket);
  1620.             debug (("#%02d: connection socket unable to receive url\n", (int) Req->ReqSocket));
  1621.             DeleteConnect (Req, FALSE);
  1622.         }
  1623.         }
  1624.     }
  1625.     }
  1626. }
  1627.  
  1628.  
  1629. /*)) */
  1630. /*(( "CacheInUse()/RequestQueued()" */
  1631.  
  1632. /* Check wheather a cache entry is used in any other request */
  1633.  
  1634. request_t *CacheInUse (cache_t *c)
  1635. {
  1636.     request_t *Req = Requests;
  1637.     int i;
  1638.  
  1639.     for (i = 0; i < MAX_REQUESTS; i++, Req++)
  1640.     if (Req->Cache == c)
  1641.         return (Req);
  1642.     return (NULL);
  1643. }
  1644.  
  1645.  
  1646. /* Initiate contact to remote host for a queued request */
  1647.  
  1648. void RequestQueued (cache_t *c)
  1649. {
  1650.     request_t *Req = Requests;
  1651.     char    Buffer [MAX_URLBUFFER];
  1652.     char    *p, *cp;
  1653.  
  1654.     assert (MAX_URLBUFFER >= 4 * MAX_URLSAVE);  /* to be on the save side */
  1655.     fprintf (LogStream, "initiating request for Url '%s'\n", c->Url);
  1656.  
  1657.     while (Req->Flags)                        /* any Flags set -> occupied */
  1658.     Req++;
  1659.     assert (Req - Requests < MAX_REQUESTS);
  1660.  
  1661.     Req->Flags  = REQ_URLDONE;
  1662.     Req->UrlSent= Req->DataSent = Req->DataRecv = 0;
  1663.     Req->Cache  = NULL;
  1664.     Req->Stream = NULL;
  1665.     RequestsFree--;
  1666.  
  1667.     for (p = c->Url, cp = Buffer; *p; )
  1668.     if (isvalidhttp (*p))
  1669.         *cp++ = *p++;
  1670.     else
  1671.     {
  1672.         sprintf (cp, "%%%02x", *p++);
  1673.         cp += 3;
  1674.     }
  1675.     *cp = '\0';
  1676.  
  1677.     sprintf (Req->UrlBuffer, "GET %s HTTP/1.0\nUser-Agent: %s\nAccept: */*\n\n", Buffer, Version);
  1678.     Req->UrlRecv = strlen (Req->UrlBuffer);
  1679.     debug (("initiating request '%s'\n", Req->UrlBuffer));
  1680.     if (c->File [0] == '@')
  1681.     c->Url [0] = '\0';
  1682.     RemCacheEntry (c);
  1683.     ServConnect (Req);
  1684. }
  1685.  
  1686.  
  1687. /*)) */
  1688. /*(( "CheckGetQueued()" */
  1689.  
  1690. /* Check number of pending requests and initiate getting of queued URLs */
  1691.  
  1692. void CheckGetQueued (void)
  1693. {
  1694.     static int Nr = 0;
  1695.     static int State = 1;
  1696.     int    Found = 1;
  1697.     cache_t *c;
  1698.  
  1699.     if (State == 3 || ! GetQueued)
  1700.     return;
  1701.     while (Found && RequestsFree > MIN_REQUESTS)
  1702.     {
  1703.     Found = 0;
  1704.     debug (("State %d\n", State));
  1705.  
  1706.     switch (State) {
  1707.     case 1:                         /* get all CACHE_QUEUED & ! CACHE_VALID */
  1708.         for (c = & Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
  1709.         if ((c->Flags & CACHE_QUEUED) && ! (c->Flags & CACHE_VALID))    /* get urls without expired caches first */
  1710.         {
  1711.             Found++;
  1712.             if (! CacheInUse (c))
  1713.             {
  1714.             RequestQueued (c);
  1715.             break;
  1716.             }
  1717.         }
  1718.         if (Nr == MAX_CACHES)
  1719.         Nr = 0;
  1720.         if (Found)
  1721.         break;
  1722.  
  1723.         Nr    = 0;                  /* no break! */
  1724.         State = 2;
  1725.  
  1726.     case 2:
  1727.         for (c = & Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
  1728.         if (c->Flags & CACHE_QUEUED)
  1729.         {
  1730.             assert (c->Flags & CACHE_VALID);
  1731.             Found++;
  1732.             if (! CacheInUse (c))
  1733.             {
  1734.             RequestQueued (c);
  1735.             break;
  1736.             }
  1737.         }
  1738.         if (! Found)
  1739.         State = 3;
  1740.         break;
  1741.  
  1742.     default:
  1743.         assert (0);
  1744.     }
  1745.     }
  1746. }
  1747.  
  1748.  
  1749. /*)) */
  1750. /*(( "DeleteInvalidCaches()" */
  1751.  
  1752. /* Shutdown: Close and delete all invalid cache entries. The cache database will be
  1753.  * in an inconsistent state after this operation... (missing queue entries) */
  1754.  
  1755. void DeleteInvalidCaches (void)
  1756. {
  1757.     cache_t *c;
  1758.     request_t *Req;
  1759.     int     i;
  1760.  
  1761.     for (i = 0, c = Caches; i < MAX_CACHES; i++, c++)
  1762.     if (c->File[0] && (c->Flags & (CACHE_VALID | CACHE_QUEUED)) +0 == 0)      /* !!! */
  1763.     {
  1764.         Req = CacheInUse (c);
  1765.         if (Req)
  1766.         {
  1767.         if (Req->Stream)                      /* close cache stream */
  1768.             fclose (Req->Stream);
  1769.         Req->Stream = NULL;
  1770.         if (c->Url[0])
  1771.         {
  1772.             fprintf (LogStream, "removing and queueing URL '%s'\n", c->Url);
  1773.             SaveCacheUrl (c);
  1774.         }
  1775.         c->Url[0] = '\0';                     /* keep the Url file (cache is queued) */
  1776.         RemCacheEntry (c);                    /* delete cache entry, data file and intermediate url files */
  1777.         }
  1778.         else
  1779.         debug (("File '%s', Url '%s', Flags 0x%x without Request!\n", c->File, c->Url, c->Flags));
  1780.     }
  1781. }
  1782.  
  1783.  
  1784. /*)) */
  1785. /*(( "main ()" */
  1786.  
  1787. /* The main routine */
  1788.  
  1789. void main (int argc, char **argv)
  1790. {
  1791.     fd_set ReadSet;
  1792.     fd_set WriteSet;
  1793.     char   *ProxyHost = NULL;
  1794.     char   *LogFile   = ".httpproxy-log";
  1795.     int    ProxyPort;
  1796.  
  1797.     PrgName = *argv++;
  1798.  
  1799.     if (--argc == 1)
  1800.     if (**argv == '?')
  1801.     {
  1802.         fprintf (stderr, "Usage: %s [proxy PROXYHOST PROXYPORT] [port PORT] [cache DIR] [del SECONDS]\n"
  1803.                  "[expire SECONDS] [reload SECONDS] [log FILE] [unread] [offline] [get]\n"
  1804.                  "The cache keyword will change the local directory.\n", PrgName);
  1805.         exit (0);
  1806.     }
  1807.  
  1808.     while (argc)
  1809.     {
  1810.     if (strcasecmp (*argv, "proxy") == 0)
  1811.     {
  1812.         if (argc < 3)
  1813.         {
  1814.         fprintf (stderr, "%s: need two arguments for 'proxy'\n", PrgName);
  1815.         exit (1);
  1816.         }
  1817.         argv++;
  1818.         argc -= 3;
  1819.         ProxyHost = *argv++;
  1820.         ProxyPort = atoi (*argv++);
  1821.     }
  1822.     else if (strcasecmp (*argv, "port") == 0)
  1823.     {
  1824.         if (argc < 2)
  1825.         {
  1826.         fprintf (stderr, "%s: need a argument for 'port'\n", PrgName);
  1827.         exit (1);
  1828.         }
  1829.         argv++;
  1830.         argc -= 2;
  1831.         ServerPort = atoi (*argv++);
  1832.     }
  1833.     else if (strcasecmp (*argv, "cache") == 0)
  1834.     {
  1835.         if (argc < 2)
  1836.         {
  1837.         fprintf (stderr, "%s: need a argument for 'cache'\n", PrgName);
  1838.         exit (1);
  1839.         }
  1840.         argv++;
  1841.         argc -= 2;
  1842.         if (chdir (*argv++))
  1843.         {
  1844.         fprintf (stderr, "%s: no directory '%s': %s\n", PrgName, argv[-1], strerror (errno));
  1845.         exit (1);
  1846.         }
  1847.     }
  1848.     else if (strcasecmp (*argv, "del") == 0)
  1849.     {
  1850.         if (argc < 2)
  1851.         {
  1852.         fprintf (stderr, "%s: need a argument for 'del'\n", PrgName);
  1853.         exit (1);
  1854.         }
  1855.         argv++;
  1856.         argc -= 2;
  1857.         DelCacheTime = atoi (*argv++);
  1858.     }
  1859.     else if (strcasecmp (*argv, "expire") == 0)
  1860.     {
  1861.         if (argc < 2)
  1862.         {
  1863.         fprintf (stderr, "%s: need a argument for 'expire'\n", PrgName);
  1864.         exit (1);
  1865.         }
  1866.         argv++;
  1867.         argc -= 2;
  1868.         ExpireCacheTime = atoi (*argv++);
  1869.     }
  1870.     else if (strcasecmp (*argv, "reload") == 0)
  1871.     {
  1872.         if (argc < 2)
  1873.         {
  1874.         fprintf (stderr, "%s: need a argument for 'reload'\n", PrgName);
  1875.         exit (1);
  1876.         }
  1877.         argv++;
  1878.         argc -= 2;
  1879.         ReloadCacheTime = atoi (*argv++);
  1880.     }
  1881.     else if (strcasecmp (*argv, "log") == 0)
  1882.     {
  1883.         if (argc < 2)
  1884.         {
  1885.         fprintf (stderr, "%s: need a argument for 'log'\n", PrgName);
  1886.         exit (1);
  1887.         }
  1888.         argv++;
  1889.         argc -= 2;
  1890.         LogFile = *argv++;
  1891.     }
  1892.     else if (strcasecmp (*argv, "unread") == 0)
  1893.     {
  1894.         argv++;
  1895.         argc--;
  1896.         CacheUnreadRequests = TRUE;
  1897.     }
  1898.     else if (strcasecmp (*argv, "offline") == 0)
  1899.     {
  1900.         argv++;
  1901.         argc--;
  1902.         if (GetQueued)
  1903.         {
  1904.         fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
  1905.         exit (1);
  1906.         }
  1907.         OffLine = 1;
  1908.     }
  1909.     else if (strcasecmp (*argv, "get") == 0)
  1910.     {
  1911.         argv++;
  1912.         argc--;
  1913.         if (OffLine)
  1914.         {
  1915.         fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
  1916.         exit (1);
  1917.         }
  1918.         GetQueued = 1;
  1919.     }
  1920.     else
  1921.     {
  1922.         fprintf (stderr, "%s: unknown option '%s'\n", PrgName, *argv);
  1923.         exit (1);
  1924.     }
  1925.     }
  1926.  
  1927.     Init (ProxyHost, ProxyPort, LogFile);
  1928.  
  1929.     BuildCache ();
  1930.     assert (FD_SETSIZE > MAX_REQUESTS * 3);
  1931.  
  1932.     /* Never return */
  1933.     for (;;)
  1934.     {
  1935.     CheckGetQueued ();
  1936.     BuildFdSets (&ReadSet, &WriteSet);
  1937.     debug (("Select... RequestsFree %d, CachesFree %d\n", RequestsFree, CachesFree));
  1938.     if (select (FD_SETSIZE, &ReadSet, &WriteSet, NULL, NULL) <= 0)
  1939.     {
  1940.         if (errno == EINTR)
  1941.         {
  1942.         fprintf (stderr, "%s: terminating due to signal\n", PrgName);
  1943.         fprintf (LogStream, "terminating due to signal\n");
  1944.         DeleteInvalidCaches ();
  1945.         fprintf (LogStream, "\n");
  1946.         exit (0);
  1947.         }
  1948.         syslog (LOG_ERR, "%s: select failed: %s", PrgName, strerror (errno));
  1949.     }
  1950.     else
  1951.     {
  1952.         ServRead (&ReadSet);
  1953.         ServWrite (&WriteSet);
  1954.     }
  1955.     }
  1956. }
  1957. /*)) */
  1958.  
  1959.